﻿using ShadowCode.Injection.Base;
using System.CodeDom.Compiler;
namespace ShadowCode.Injection.Injections;

public class InjectionAttribute(TransformData data) : AttributeBase<InjectionUnitBase>(data)
{
    private ReplaceGeneric? replaceGeneric;

    protected override InjectionUnitBase GetTarget() =>
        AttributeSyntax.Ancestors().Select(CreateUnit).OfType<InjectionUnitBase>().FirstOrDefault()
        ?? throw new Exception($"找不到类型或构造函数: {data.SyntaxTree}");

    private UnitBase? CreateUnit(SyntaxNode node) => node switch
    {
        ClassDeclarationSyntax @class => new ClassUnit(data, @class),
        ConstructorDeclarationSyntax ctor => new ConstructorUnit(data, ctor),
        _ => default
    };

    public override string GroupKey => Target.GroupKey;

    public INamedTypeSymbol GetServiceType() =>
       GenericArgument ?? PropertyServiceType ?? Target.Declaration;

    public ReplaceGeneric ReplaceGeneric =>
        replaceGeneric ??= new(GetServiceType(), Target.Declaration);

    #region 属性

    public MsServiceLifetime Lifetime { get; private set; } = MsServiceLifetime.Scoped;

    public INamedTypeSymbol? PropertyServiceType { get; private set; }

    public FactoryUnit? Factory { get; private set; }

    public object? Keyed { get; private set; }

    public bool TryAdd { get; private set; }

    public int Index { get; private set; }

    public bool IsKeyed { get; private set; }

    #endregion

    #region SetProperty   

    protected override void SetProperty(SeparatedSyntaxList<AttributeArgumentSyntax> arguments)
    {
        SetLifetime(arguments[0].Expression);
        foreach (var item in arguments.Skip(1)) SetProperty(item);
    }

    private void SetLifetime(ExpressionSyntax expression)
    {
        var value = data.GetConstantStringValue(expression);

        if (byte.TryParse($"{value}", out var res))
            Lifetime = (MsServiceLifetime)res;
    }

    private void SetProperty(AttributeArgumentSyntax node)
    {
        var ne = node.NameEquals;
        if (ne is null) return;
        var propName = ne.Name.Identifier.Text;
        var exp = node.Expression;
        var value = data.GetConstantValue(exp);
        var valueString = $"{value}";

        switch (propName)
        {
            case "Keyed":
                Keyed = value.Value;
                IsKeyed = Keyed != null;
                break;

            case "TryAdd" when bool.TryParse(valueString, out var tryAdd):
                TryAdd = tryAdd;
                break;

            case "Factory":
                Factory = new FactoryUnit(data, exp);
                break;

            case "Index" when int.TryParse(valueString, out var index):
                Index = index;
                break;

            case "ServiceType":
                SetPropertyServiceType(exp);
                break;

            default: throw new NotImplementedException($"待补充'{propName}'");
        }
    }

    private void SetPropertyServiceType(ExpressionSyntax exp)
    {
        if (exp is not TypeOfExpressionSyntax tes)
            throw Error.CreateUnexpected(exp);

        PropertyServiceType = data.GetTypeSymbol(tes.Type) as INamedTypeSymbol;
    }

    #endregion

    internal override void Buid(IndentedTextWriter writer)
    {
        writer.WriteLine($"// {AttributeSyntax?.GetFileLineInfo()}");
        writer.WriteScope(() => BuidCode(writer));
    }

    private void BuidCode(IndentedTextWriter writer)
    {
        var arguments = new List<string>();
        var serviceType = GetServiceType();
        var implementType = ReplaceGeneric.TmplementType;
        var serviceTypeCode = $"typeof({serviceType.TryUnGeneric().ToDisplayString()})";

        if (Factory != null)
        {
            AddServiceType();
            AddKeyed();
            arguments.Add(Factory.GetMethodPath(this));
        }
        else if (Target.CanSimple)
        {
            // 简单表达式
            if (serviceType.EqualsDefault(implementType))
            {
                arguments.Add(serviceTypeCode);
                AddKeyed();
            }

            // typeof(TInterface<,>)
            else if (serviceTypeCode.Contains("?"))
            {
                AddServiceType(serviceTypeCode.Replace("?", ""));
                AddKeyed();
                AddImplementType(implementType);
            }

            // 主构函数
            else
            {
                AddServiceType();
                AddKeyed();
                AddImplementType(implementType);
            }
        }
        // 未包含泛型实参(抛出错误 无法使用)
        else if (serviceType.IsGenericType && serviceType.IsOriginalGenericType())
        {
            throw Error.RequireGenericArguments.CreateError("需要指定泛型实参！", AttributeSyntax);
        }
        else
        {
            AddServiceType();
            AddKeyed();
            Target.ImplementBuild(this, writer, arguments);
        }

        writer.WriteLine($"{GetMethodName()}({arguments.JoinString()});");

        void AddServiceType(string? type = null)
        {
            arguments.Add("serviceType");
            writer.WriteLine($"var serviceType = {type ?? serviceTypeCode};");
        }

        void AddImplementType(INamedTypeSymbol implementType)
        {
            var implementTypeCode = implementType.TryUnGeneric().ToDisplayString().Replace("?", "");
            writer.WriteLine($"var implementType = typeof({implementTypeCode});");
            arguments.Add("implementType");
        }

        void AddKeyed() => SendKeyed(writer, arguments);
    }

    private void SendKeyed(IndentedTextWriter writer, List<string> arguments)
    {
        if (Keyed is null) return;
        var type = Keyed.GetType();
        arguments.Add("keyed");

        var s = type.IsPrimitive ? "" : "\"";
        writer.WriteLine($"object? keyed = {s}{Keyed}{s};");
    }

    private string GetMethodName()
    {
        var tryAdd = TryAdd ? "TryAdd" : "Add";
        var keyed = IsKeyed ? "Keyed" : "";
        var lifetime = Lifetime.ToString();
        return $"service.{tryAdd}{keyed}{lifetime}";
    }

}